﻿//-----------------------------------------------------------------------
// <copyright file="WeatherWebPart.cs" company="Danny Jessee">
//     This code is released under the Microsoft Public License (Ms-PL).
//     For more information, see http://facebookwebparts.codeplex.com/license.
// </copyright>
//-----------------------------------------------------------------------
namespace FacebookWebParts
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using Facebook;
    using Microsoft.IdentityModel.Claims;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebControls;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    
    /// <summary>
    /// Retrieves current weather conditions based on the Facebook user's current location.
    /// </summary>
    [ToolboxItemAttribute(false)]
    public class WeatherWebPart : WebPart
    {
        /// <summary>
        /// String used to hold the Facebook Graph API AccessToken. 
        /// </summary>
        private string accessToken = string.Empty;

        /// <summary>
        /// String used to hold the Facebook user's current city.
        /// </summary>
        private string city = string.Empty;

        /// <summary>
        /// String used to hold the Facebook user's current state.
        /// </summary>
        private string state = string.Empty;

        /// <summary>
        /// Label used for displaying output and error information.
        /// </summary>
        private Label lblStatus = new Label();

        /// <summary>
        /// String value containing the Weather Underground API key.
        /// </summary>
        private string wunderApiKey = string.Empty;

        /// <summary>
        /// String value containing the Facebook AccessToken claim URI.
        /// </summary>
        private string accessTokenClaimUri = string.Empty;

        /// <summary>
        /// Gets or sets the string value containing the URI of the Facebook Graph API AccessToken claim.
        /// By default, this URI is http://www.facebook.com/claims/AccessToken.
        /// </summary>
        [WebBrowsable(true), Category("Custom Properties")]
        [WebDisplayName("Facebook Access Token Claim URI")]
        [Personalizable(PersonalizationScope.Shared)]
        public string FacebookAccessTokenClaimUri
        {
            get
            {
                if (string.IsNullOrEmpty(this.accessTokenClaimUri))
                {
                    this.accessTokenClaimUri = "http://www.facebook.com/claims/AccessToken";
                }

                return this.accessTokenClaimUri;
            }

            set
            {
                this.accessTokenClaimUri = value;
            }
        }

        /// <summary>
        /// Gets or sets the string value containing the Weather Underground API key.
        /// Please register for your own API key here: http://www.wunderground.com/weather/api/
        /// </summary>
        [WebBrowsable(true), Category("Custom Properties")]
        [WebDisplayName("Weather Underground API Key")]
        [Personalizable(PersonalizationScope.Shared)]
        public string WeatherUndergroundAPIKey
        {
            get { return this.wunderApiKey; }
            set { this.wunderApiKey = value; }
        }

        /// <summary>
        /// Gets or sets the string value containing the Facebook Graph API AccessToken.
        /// </summary>
        private string FacebookAccessToken
        {
            get
            {
                if (string.IsNullOrEmpty(this.accessToken))
                {
                    try
                    {
                        IClaimsPrincipal claimsPrincipal = Page.User as IClaimsPrincipal;
                        IClaimsIdentity claimsIdentity = (IClaimsIdentity)claimsPrincipal.Identity;

                        this.accessToken = (from c in claimsIdentity.Claims
                                            where c.ClaimType.Equals(this.FacebookAccessTokenClaimUri)
                                            select c.Value).FirstOrDefault();
                    }
                    catch (Exception)
                    {
                        this.accessToken = string.Empty;
                    }
                }

                return this.accessToken;
            }

            set
            {
                this.accessToken = value;
            }
        }

        /// <summary>
        /// Parse the current Facebook user's city and state, then pass that information to the Weather
        /// Underground API.
        /// NOTE: Please register for an API key at http://www.wunderground.com/weather/api/.
        /// </summary>
        protected override void CreateChildControls()
        {
            try
            {
                if (!string.IsNullOrEmpty(this.FacebookAccessTokenClaimUri))
                {
                    if (!string.IsNullOrEmpty(this.WeatherUndergroundAPIKey))
                    {
                        // Get current location city
                        string userCity = this.GetFacebookLocation(this.FacebookAccessToken);
                        if (!string.IsNullOrEmpty(userCity))
                        {
                            // Parse city, state and make Wunderground request
                            if (userCity.Contains(", "))
                            {
                                string[] cityState = userCity.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
                                if (cityState.GetLength(0) == 2)
                                {
                                    this.city = cityState[0];
                                    this.state = cityState[1];

                                    // http://api.wunderground.com/api/KEY/FEATURE/[FEATURE…]/q/QUERY.FORMAT
                                    string wunderQueryUrl = string.Format("http://api.wunderground.com/api/{0}/geolookup/conditions/forecast/q/{1}/{2}.json", this.WeatherUndergroundAPIKey, this.state, this.city);

                                    string json = new WebClient().DownloadString(wunderQueryUrl);
                                    JObject obs = JObject.Parse(json);
                                    this.Controls.Add(new LiteralControl("<div style=\"text-align: center\">"));
                                    this.Controls.Add(new LiteralControl("<font size=3>Current conditions in " + userCity + "</font><br/>"));
                                    this.Controls.Add(new LiteralControl("<img src=\"" + obs["current_observation"]["icon_url"] as string + "\" alt=" + obs["current_observation"]["weather"] as string + "\"><br/>"));
                                    this.Controls.Add(new LiteralControl("<font size=4><b>" + obs["current_observation"]["weather"] as string + "</b></font><br/>"));
                                    this.Controls.Add(new LiteralControl("<font size=5><b>" + obs["current_observation"]["temp_f"] as string + "°F</b></font><br/>"));
                                    this.Controls.Add(new LiteralControl("<font size=2>Relative Humidity: " + obs["current_observation"]["relative_humidity"] as string + "</font><br/>"));
                                    this.Controls.Add(new LiteralControl("<font size=2>Dewpoint: " + obs["current_observation"]["dewpoint_f"] as string + "°F</font><br/>"));
                                    this.Controls.Add(new LiteralControl("<font size=2>Wind: " + obs["current_observation"]["wind_string"] as string + "</font><br/>"));
                                    this.Controls.Add(new LiteralControl("<font size=2>Precip Today: " + obs["current_observation"]["precip_today_in"] as string + "\"</font><br/>"));
                                    this.Controls.Add(new LiteralControl("<font size=2>" + obs["current_observation"]["observation_time"].ToString() + "</font><br/>"));
                                    this.Controls.Add(new LiteralControl("</div>"));
                                }
                            }
                        }
                    }
                    else
                    {
                        // The Weather Underground API Key web part property has not been set
                        this.Controls.Add(new LiteralControl("<font size=2>Please register for a Weather Underground API Key and set its value in the web part properties.</font>"));
                    }
                }
                else
                {
                    this.Controls.Add(new LiteralControl("<font size=2>Please ensure the Facebook AccessToken claim URI is populated and that you are signed in via Facebook.</font>"));
                }
            }
            catch (Exception ex)
            {
                this.lblStatus.Text = "Error: " + ex.Message + "<br/>" + ex.StackTrace;
                this.Controls.Add(this.lblStatus);
            }
        }

        /// <summary>
        /// Parses the Facebook user's current location.
        /// </summary>
        /// <param name="token">Facebook Graph API AccessToken.</param>
        /// <returns>String containing "city, state" of user's current location.</returns>
        private string GetFacebookLocation(string token)
        {
            if (!string.IsNullOrEmpty(token))
            {
                var client = new Facebook.FacebookClient(token);
                var me = (IDictionary<string, object>)client.Get("me");
                JsonObject location = me["location"] as JsonObject;
                return (string)location["name"];
            }

            return string.Empty;
        }
    }
}